home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Format 1995 June
/
MacFormat 25.iso
/
Shareware City
/
Developers
/
OutOfPhase1.1 Source
/
OutOfPhase Folder
/
OscBankPlayer.c
< prev
next >
Wrap
Text File
|
1994-12-10
|
30KB
|
880 lines
/* OscBankPlayer.c */
/*****************************************************************************/
/* */
/* Out Of Phase: Digital Music Synthesis on General Purpose Computers */
/* Copyright (C) 1994 Thomas R. Lawrence */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* */
/* Thomas R. Lawrence can be reached at tomlaw@world.std.com. */
/* */
/*****************************************************************************/
#include "MiscInfo.h"
#include "Audit.h"
#include "Debug.h"
#include "Definitions.h"
#define ShowMeFrozenNoteRec
#define ShowMe_NoteObjectRec
#include "OscBankPlayer.h"
#include "LFOGenerator.h"
#include "IncrementalParameterUpdator.h"
#include "LFOSpecifier.h"
#include "LFOListSpecifier.h"
#include "Memory.h"
#include "Array.h"
#include "OscillatorListSpecifier.h"
#include "OscillatorSpecifier.h"
#include "InstrumentStructure.h"
#include "SampleOscControl.h"
#include "WaveTableOscControl.h"
#include "DeterminedNoteStructure.h"
#include "NoteObject.h"
#include "FloatingPoint.h"
#include "Frequency.h"
#include "ErrorDaemon.h"
typedef struct
{
/* this is the reference to the template object */
void* TemplateReference;
/* perform one envelope update cycle */
void (*UpdateEnvelopes)(void* State);
/* dispose of the state record */
void (*DisposeState)(void* State);
/* dispose of the information template */
void (*DisposeTemplate)(void* Template);
/* create a new state object. */
void* (*NewState)(void* Template,
float FreqForMultisampling, float Accent1, float Accent2,
float Accent3, float Accent4, float Loudness, float HurryUp,
long* PreOriginTimeOut, float StereoPosition,
float InitialFrequency, float PitchDisplacementDepthLimit,
float PitchDisplacementRateLimit,
long PitchDisplacementStartPoint);
/* fix up pre-origin time for the state object */
void (*FixUpStatePreOrigin)(void* State, long ActualPreOrigin);
/* set a new frequency for a state object. used for portamento */
/* and modulation of frequency (vibrato) */
void (*StateNewFrequency)(void* State, float NewFrequencyHertz);
/* send a key-up signal to one of the oscillators */
void (*KeyUpSustain1)(void* State);
void (*KeyUpSustain2)(void* State);
void (*KeyUpSustain3)(void* State);
/* restart a oscillator. this is used for tie continuations */
void (*RestartState)(void* State, float NewFreqMultisampling,
float NewAccent1, float NewAccent2, float NewAccent3,
float NewAccent4, float NewLoudness, float NewHurryUp,
MyBoolean RetriggerEnvelopes, float NewStereoPosition,
float NewInitialFrequency,
float PitchDisplacementDepthLimit,
float PitchDisplacementRateLimit,
long PitchDisplacementStartPoint);
/* generate a sequence of samples (called for each envelope clock) */
void (*GenSamples)(void* State, long SampleCount,
largefixedsigned* RawBuffer);
/* find out if the oscillator has finished yet */
MyBoolean (*IsItFinished)(void* State);
} OscBankVectorRec;
struct OscBankTemplateRec
{
/* general parameters */
MyBoolean StereoOutput;
float OverallVolumeScalingFactor;
long SamplingRate;
float EnvelopeUpdateRate;
MyBoolean TimeInterpolation;
MyBoolean WaveInterpolation;
/* parameter updator for track (so we can set up individual notes properly) */
IncrParamUpdateRec* ParamUpdator;
/* template for the pitch displacement LFO */
LFOListSpecRec* PitchLFOTemplate;
/* instrument overall loudness */
float InstrOverallLoudness;
/* list of template records describing each oscillator */
OscBankVectorRec* TemplateArray;
/* this is the number of oscillators in the array */
long NumOscillatorsInBank;
};
typedef struct OscStateRec
{
/* this is a reference to the state object for this oscillator */
void* StateReference;
/* copy of the routine vectors */
OscBankVectorRec Template;
/* next oscillator in the list */
struct OscStateRec* Next;
} OscStateRec;
struct OscStateBankRec
{
/* what are we derived from */
OscBankTemplateRec* BankTemplate;
/* list of oscillators that this oscillator bank is comprised of */
OscStateRec* OscillatorList;
/* this calculates the differential values for periodic pitch displacements */
LFOGenRec* PitchLFO;
/* if this object ties to a note, then this is the note to tie to. this is */
/* used for finding existing oscillators for tie continuations. */
struct NoteObjectRec* TieToNote;
/* portamento control parameters */
long PortamentoCounter; /* 0 = done */
long TotalPortamentoTicks;
float InitialFrequency;
float FinalFrequency;
float CurrentFrequency;
/* True = portamento linear to Hertz; False = portamento linear to half-steps */
MyBoolean PortamentoHertz;
/* various counters (in terms of envelope ticks) */
/* negative = expired */
long Release1Countdown;
long Release2Countdown;
long Release3Countdown;
long PitchLFOStartCountdown;
/* next oscillator bank in the list */
struct OscStateBankRec* Next;
};
static OscStateRec* StateFreeList = NIL;
static OscStateBankRec* StateBankFreeList = NIL;
/* flush all cached oscillator state bank records */
void FlushCachedOscStateBankRecords(void)
{
while (StateBankFreeList != NIL)
{
OscStateBankRec* Temp;
Temp = StateBankFreeList;
StateBankFreeList = StateBankFreeList->Next;
ReleasePtr((char*)Temp);
}
while (StateFreeList != NIL)
{
OscStateRec* Temp;
Temp = StateFreeList;
StateFreeList = StateFreeList->Next;
ReleasePtr((char*)Temp);
}
}
#if DEBUG
static void ValidateOscState(OscStateRec* State)
{
OscStateRec* Scan;
CheckPtrExistence(State);
Scan = StateFreeList;
while (Scan != NIL)
{
if (Scan == State)
{
PRERR(ForceAbort,"ValidateOscState: state object is on free list");
}
Scan = Scan->Next;
}
}
#else
#define ValidateOscState(x) ((void)0)
#endif
#if DEBUG
static void ValidateOscStateBank(OscStateBankRec* StateBank)
{
OscStateBankRec* Scan;
CheckPtrExistence(StateBank);
Scan = StateBankFreeList;
while (Scan != NIL)
{
if (Scan == StateBank)
{
PRERR(ForceAbort,"ValidateOscStateBank: state bank object is on free list");
}
Scan = Scan->Next;
}
}
#else
#define ValidateOscStateBank(x) ((void)0)
#endif
/* construct an oscillator bank template record. various parameters are passed in */
/* which are needed for synthesis. ParameterUpdator is the parameter information */
/* record for the whole track of which this is a part. */
OscBankTemplateRec* NewOscBankTemplate(struct InstrumentRec* InstrumentDefinition,
MyBoolean StereoFlag, LargeBCDType OverallVolumeScalingReciprocal,
long SamplingRate, float EnvelopeRate, MyBoolean TimeInterp,
MyBoolean WaveInterp, struct IncrParamUpdateRec* ParameterUpdator,
ErrorDaemonRec* ErrorDaemon)
{
OscBankTemplateRec* Template;
OscillatorListRec* OscillatorListObject;
long Scan;
CheckPtrExistence(InstrumentDefinition);
CheckPtrExistence(ParameterUpdator);
CheckPtrExistence(ErrorDaemon);
Template = (OscBankTemplateRec*)AllocPtrCanFail(sizeof(OscBankTemplateRec),
"OscBankTemplateRec");
if (Template == NIL)
{
FailurePoint1:
return NIL;
}
/* the oscillator bank template contains all of the information needed for */
/* constructing oscillators as notes are to be executed. */
/* number of oscillators in a bank. */
OscillatorListObject = GetInstrumentOscillatorList(InstrumentDefinition);
/* get LFO information */
Template->PitchLFOTemplate = GetInstrumentFrequencyLFOList(InstrumentDefinition);
/* vector containing templates for all of the oscillators */
Template->TemplateArray = (OscBankVectorRec*)AllocPtrCanFail(
sizeof(OscBankVectorRec) * GetOscillatorListLength(OscillatorListObject),
"OscBankVectorRec");
if (Template->TemplateArray == NIL)
{
FailurePoint4:
ReleasePtr((char*)Template);
goto FailurePoint1;
}
/* build entry for each oscillator */
Template->NumOscillatorsInBank = GetOscillatorListLength(OscillatorListObject);
for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
{
OscillatorRec* Osc = GetOscillatorFromList(OscillatorListObject,Scan);
CheckPtrExistence(Osc);
switch (OscillatorGetWhatKindItIs(Osc))
{
default:
EXECUTE(PRERR(ForceAbort,"NewOscBankTemplate: bad oscillator type"));
break;
case eOscillatorSampled:
PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
sizeof(Template->TemplateArray[Scan]));
Template->TemplateArray[Scan].TemplateReference
= NewSampleTemplate(Osc,EnvelopeRate,SamplingRate,StereoFlag,
TimeInterp,WaveInterp,ErrorDaemon);
if (Template->TemplateArray[Scan].TemplateReference == NIL)
{
FailurePoint5:
for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
{
(*Template->TemplateArray[Scan]
.DisposeTemplate)(Template->TemplateArray[
Scan].TemplateReference);
}
goto FailurePoint4;
}
Template->TemplateArray[Scan].UpdateEnvelopes
= (void (*)(void*))&UpdateSampleEnvelopes;
Template->TemplateArray[Scan].DisposeState
= (void (*)(void*))&DisposeSampleState;
Template->TemplateArray[Scan].DisposeTemplate
= (void (*)(void*))&DisposeSampleTemplate;
Template->TemplateArray[Scan].NewState
= (void* (*)(void*,float,float,float,float,float,
float,float,long*,float,float,float,float,long))&NewSampleState;
Template->TemplateArray[Scan].FixUpStatePreOrigin
= (void (*)(void*,long))&FixUpSampleStatePreOrigin;
Template->TemplateArray[Scan].StateNewFrequency
= (void (*)(void*,float))&SampleStateNewFrequency;
Template->TemplateArray[Scan].KeyUpSustain1
= (void (*)(void*))&SampleKeyUpSustain1;
Template->TemplateArray[Scan].KeyUpSustain2
= (void (*)(void*))&SampleKeyUpSustain2;
Template->TemplateArray[Scan].KeyUpSustain3
= (void (*)(void*))&SampleKeyUpSustain3;
Template->TemplateArray[Scan].RestartState
= (void (*)(void*,float,float,float,float,float,float,float,
MyBoolean,float,float,float,float,long))&RestartSampleState;
Template->TemplateArray[Scan].GenSamples
= (void (*)(void*,long,largefixedsigned*))&SampleGenSamples;
Template->TemplateArray[Scan].IsItFinished
= (MyBoolean (*)(void*))&SampleIsItFinished;
break;
case eOscillatorWaveTable:
PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
sizeof(Template->TemplateArray[Scan]));
Template->TemplateArray[Scan].TemplateReference
= NewWaveTableTemplate(Osc,EnvelopeRate,SamplingRate,StereoFlag,
TimeInterp,WaveInterp,ErrorDaemon);
if (Template->TemplateArray[Scan].TemplateReference == NIL)
{
goto FailurePoint5;
}
Template->TemplateArray[Scan].UpdateEnvelopes
= (void (*)(void*))&UpdateWaveTableEnvelopes;
Template->TemplateArray[Scan].DisposeState
= (void (*)(void*))&DisposeWaveTableState;
Template->TemplateArray[Scan].DisposeTemplate
= (void (*)(void*))&DisposeWaveTableTemplate;
Template->TemplateArray[Scan].NewState
= (void* (*)(void*,float,float,float,float,float,
float,float,long*,float,float,float,float,long))&NewWaveTableState;
Template->TemplateArray[Scan].FixUpStatePreOrigin
= (void (*)(void*,long))&FixUpWaveTableStatePreOrigin;
Template->TemplateArray[Scan].StateNewFrequency
= (void (*)(void*,float))&WaveTableStateNewFrequency;
Template->TemplateArray[Scan].KeyUpSustain1
= (void (*)(void*))&WaveTableKeyUpSustain1;
Template->TemplateArray[Scan].KeyUpSustain2
= (void (*)(void*))&WaveTableKeyUpSustain2;
Template->TemplateArray[Scan].KeyUpSustain3
= (void (*)(void*))&WaveTableKeyUpSustain3;
Template->TemplateArray[Scan].RestartState
= (void (*)(void*,float,float,float,float,float,float,float,
MyBoolean,float,float,float,float,long))&RestartWaveTableState;
Template->TemplateArray[Scan].GenSamples
= (void (*)(void*,long,largefixedsigned*))&WaveTableGenSamples;
Template->TemplateArray[Scan].IsItFinished
= (MyBoolean (*)(void*))&WaveTableIsItFinished;
break;
}
}
/* playback control parameters */
Template->StereoOutput = StereoFlag;
Template->OverallVolumeScalingFactor = 1.0 / LargeBCD2Single(OverallVolumeScalingReciprocal);
Template->SamplingRate = SamplingRate;
Template->EnvelopeUpdateRate = EnvelopeRate;
Template->TimeInterpolation = TimeInterp;
Template->WaveInterpolation = WaveInterp;
Template->ParamUpdator = ParameterUpdator;
Template->InstrOverallLoudness = GetInstrumentOverallLoudness(InstrumentDefinition);
return Template;
}
/* dispose of the template */
void DisposeOscBankTemplate(OscBankTemplateRec* Template)
{
long Scan;
CheckPtrExistence(Template);
for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
{
PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
sizeof(Template->TemplateArray[Scan]));
(*Template->TemplateArray[Scan].DisposeTemplate)(Template->
TemplateArray[Scan].TemplateReference);
}
ReleasePtr((char*)Template->TemplateArray);
ReleasePtr((char*)Template);
}
/* construct a new oscillator bank state object based on the note. the note is */
/* assumed to start "now" in terms of the parameters in the ParameterUpdator. */
/* the ScanningGapWidth is the number of envelope clock ticks in the current scanning */
/* gap. this is used to determine how far later than "now" in terms of the back */
/* edge of the scanning gap (different from above) the osc bank should start playing. */
/* *WhenToStartPlayingOut returns the number of envelope ticks after the back edge */
/* of the scanning gap that the note should be started. */
/* <already played> | <scanning gap> | <not yet analyzed> */
/* time ----> time ----> time ----> time ----> time ----> time ----> */
/* ^A ^B */
/* point A is the back edge of the scanning gap. as this edge moves forward in time, */
/* oscillator bank state objects are removed from the queue and playback is commenced */
/* for them. */
/* point B is the front edge of the scanning gap. as this edge moves forward in time, */
/* notes are extracted from the track and state bank objects are created for them. */
/* ParameterUpdator always reflects parameters at this point in time. */
OscStateBankRec* NewOscBankState(OscBankTemplateRec* Template,
long* WhenToStartPlayingOut, struct NoteObjectRec* Note,
float EnvelopeTicksPerDurationTick)
{
OscStateBankRec* State;
long Scan;
FrozenNoteRec* FrozenNote;
long StartPointAdjust;
long MaxOscillatorPreOriginTime;
OscStateRec* OneState;
long ThisPreOriginTime;
CheckPtrExistence(Template);
CheckPtrExistence(Note);
if (StateBankFreeList != NIL)
{
State = StateBankFreeList;
StateBankFreeList = StateBankFreeList->Next;
}
else
{
State = (OscStateBankRec*)AllocPtrCanFail(sizeof(OscStateBankRec),"OscStateBankRec");
if (State == NIL)
{
FailurePoint1:
return NIL;
}
}
EXECUTE(State->Next = (OscStateBankRec*)0x81818181;)
State->BankTemplate = Template;
/* freeze the parameters */
FrozenNote = FixNoteParameters(Template->ParamUpdator,Note,&StartPointAdjust,
Template->OverallVolumeScalingFactor,EnvelopeTicksPerDurationTick);
if (FrozenNote == NIL)
{
FailurePoint2:
State->Next = StateBankFreeList;
StateBankFreeList = State;
goto FailurePoint1;
}
/* list of oscillators that this oscillator bank is comprised of */
State->OscillatorList = NIL;
MaxOscillatorPreOriginTime = 0;
for (Scan = 0; Scan < Template->NumOscillatorsInBank; Scan += 1)
{
/* allocate the new record */
if (StateFreeList != NIL)
{
OneState = StateFreeList;
StateFreeList = StateFreeList->Next;
}
else
{
OneState = (OscStateRec*)AllocPtrCanFail(sizeof(OscStateRec),"OscStateRec");
if (OneState == NIL)
{
FailurePoint3:
while (State->OscillatorList != NIL)
{
/* delink object */
OneState = State->OscillatorList;
State->OscillatorList = State->OscillatorList->Next;
/* dispose members */
(*OneState->Template.DisposeState)(OneState->StateReference);
/* stick on free list */
OneState->Next = StateFreeList;
StateFreeList = OneState;
}
goto FailurePoint2;
}
}
/* copy over the function vectors */
PRNGCHK(Template->TemplateArray,&(Template->TemplateArray[Scan]),
sizeof(Template->TemplateArray[Scan]));
OneState->Template = Template->TemplateArray[Scan];
/* create the oscillator */
OneState->StateReference = (OneState->Template.NewState)(
OneState->Template.TemplateReference,FrozenNote->MultisampleFrequency,
FrozenNote->Accent1,FrozenNote->Accent2,FrozenNote->Accent3,FrozenNote->Accent4,
FrozenNote->LoudnessAdjust * Template->InstrOverallLoudness,
FrozenNote->HurryUpFactor,&ThisPreOriginTime,FrozenNote->StereoPosition,
FrozenNote->NominalFrequency,FrozenNote->PitchDisplacementDepthLimit,
FrozenNote->PitchDisplacementRateLimit,FrozenNote->PitchDisplacementStartPoint);
if (OneState->StateReference == NIL)
{
FailurePoint3a:
OneState->Next = StateFreeList;
StateFreeList = OneState;
goto FailurePoint3;
}
if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
{
MaxOscillatorPreOriginTime = ThisPreOriginTime;
}
/* link it in */
OneState->Next = State->OscillatorList;
State->OscillatorList = OneState;
}
/* this calculates the differential values for periodic pitch displacements */
State->PitchLFO = NewLFOGenerator(Template->PitchLFOTemplate,&ThisPreOriginTime,
FrozenNote->Accent1,FrozenNote->Accent2,FrozenNote->Accent3,FrozenNote->Accent4,
FrozenNote->NominalFrequency,FrozenNote->HurryUpFactor,
Template->EnvelopeUpdateRate,FrozenNote->PitchDisplacementDepthLimit,
FrozenNote->PitchDisplacementRateLimit,FrozenNote->MultisampleFrequency);
if (State->PitchLFO == NIL)
{
FailurePoint4:
goto FailurePoint3;
}
if (ThisPreOriginTime > MaxOscillatorPreOriginTime)
{
MaxOscillatorPreOriginTime = ThisPreOriginTime;
}
/* if this object ties to a note, then this is the note to tie to. this is */
/* used for finding existing oscillators for tie continuations. */
State->TieToNote = Note->a.Note.Tie;
/* portamento control parameters */
State->PortamentoCounter = 0;
State->CurrentFrequency = FrozenNote->NominalFrequency;
/* fix up pre-origin times */
OneState = State->OscillatorList;
while (OneState != NIL)
{
(*OneState->Template.FixUpStatePreOrigin)(OneState->StateReference,
MaxOscillatorPreOriginTime);
OneState = OneState->Next;
}
LFOGeneratorFixEnvelopeOrigins(State->PitchLFO,MaxOscillatorPreOriginTime);
/* various counters (in terms of envelope ticks) */
if (State->TieToNote == NIL)
{
State->Release1Countdown = FrozenNote->ReleasePoint1
+ MaxOscillatorPreOriginTime;
State->Release2Countdown = FrozenNote->ReleasePoint2
+ MaxOscillatorPreOriginTime;
State->Release3Countdown = FrozenNote->ReleasePoint3
+ MaxOscillatorPreOriginTime;
}
else
{
/* for ties, only honor releases from start */
if (FrozenNote->Release1FromStart)
{
State->Release1Countdown = FrozenNote->ReleasePoint1
+ MaxOscillatorPreOriginTime;
}
else
{
State->Release1Countdown = -1;
}
if (FrozenNote->Release2FromStart)
{
State->Release2Countdown = FrozenNote->ReleasePoint2
+ MaxOscillatorPreOriginTime;
}
else
{
State->Release2Countdown = -1;
}
if (FrozenNote->Release3FromStart)
{
State->Release3Countdown = FrozenNote->ReleasePoint3
+ MaxOscillatorPreOriginTime;
}
else
{
State->Release3Countdown = -1;
}
}
State->PitchLFOStartCountdown = FrozenNote->PitchDisplacementStartPoint
/*+ MaxOscillatorPreOriginTime*/;
/* pre origin relationship must be preserved for pitch LFO trigger */
/* clean up */
DisposeFrozenNote(FrozenNote);
*WhenToStartPlayingOut = StartPointAdjust - MaxOscillatorPreOriginTime;
return State;
}
/* this is used for resetting a note for a tie */
/* the FrozenNote object is NOT disposed */
MyBoolean ResetOscBankState(OscStateBankRec* State,
struct FrozenNoteRec* FrozenNote,
float EnvelopeTicksPerDurationTick)
{
OscStateRec* OneState;
MyBoolean RetriggerEnvelopes;
CheckPtrExistence(State);
ValidateOscStateBank(State);
CheckPtrExistence(FrozenNote);
RetriggerEnvelopes = ((FrozenNote->OriginalNote->Flags
& eRetriggerEnvelopesOnTieFlag) != 0);
/* go through the oscillators and retrigger them */
OneState = State->OscillatorList;
while (OneState != NIL)
{
(*OneState->Template.RestartState)(OneState->StateReference,
FrozenNote->MultisampleFrequency,FrozenNote->Accent1,FrozenNote->Accent2,
FrozenNote->Accent3,FrozenNote->Accent4,FrozenNote->LoudnessAdjust
* State->BankTemplate->InstrOverallLoudness,FrozenNote->HurryUpFactor,
RetriggerEnvelopes,FrozenNote->StereoPosition,
FrozenNote->NominalFrequency,FrozenNote->PitchDisplacementDepthLimit,
FrozenNote->PitchDisplacementRateLimit,FrozenNote->PitchDisplacementStartPoint);
OneState = OneState->Next;
}
LFOGeneratorRetriggerFromOrigin(State->PitchLFO,FrozenNote->Accent1,
FrozenNote->Accent2,FrozenNote->Accent3,FrozenNote->Accent4,
FrozenNote->NominalFrequency,FrozenNote->HurryUpFactor,
FrozenNote->PitchDisplacementDepthLimit,
FrozenNote->PitchDisplacementRateLimit,RetriggerEnvelopes);
/* if this object ties to a note, then this is the note to tie to. this is */
/* used for finding existing oscillators for tie continuations. */
State->TieToNote = FrozenNote->OriginalNote->a.Note.Tie;
/* portamento control parameters */
if (FrozenNote->PortamentoDuration > 0)
{
State->PortamentoCounter = FrozenNote->PortamentoDuration;
State->TotalPortamentoTicks = FrozenNote->PortamentoDuration;
State->InitialFrequency = State->CurrentFrequency; /* save current pitch */
State->FinalFrequency = FrozenNote->NominalFrequency;
State->PortamentoHertz = ((FrozenNote->OriginalNote->Flags
& ePortamentoHertzNotHalfsteps) != 0);
}
else
{
State->PortamentoCounter = 0;
State->CurrentFrequency = FrozenNote->NominalFrequency;
}
/* various counters (in terms of envelope ticks) */
if (State->TieToNote == NIL)
{
State->Release1Countdown = FrozenNote->ReleasePoint1;
State->Release2Countdown = FrozenNote->ReleasePoint2;
State->Release3Countdown = FrozenNote->ReleasePoint3;
}
else
{
/* for ties, only honor releases from start */
if (FrozenNote->Release1FromStart)
{
State->Release1Countdown = FrozenNote->ReleasePoint1;
}
else
{
State->Release1Countdown = -1;
}
if (FrozenNote->Release2FromStart)
{
State->Release2Countdown = FrozenNote->ReleasePoint2;
}
else
{
State->Release2Countdown = -1;
}
if (FrozenNote->Release3FromStart)
{
State->Release3Countdown = FrozenNote->ReleasePoint3;
}
else
{
State->Release3Countdown = -1;
}
}
if (RetriggerEnvelopes)
{
State->PitchLFOStartCountdown = FrozenNote->PitchDisplacementStartPoint;
}
else
{
State->PitchLFOStartCountdown = -1;
}
return True;
}
/* get rid of a state bank */
void DisposeOscStateBank(OscStateBankRec* State)
{
OscStateRec* OneStateScan;
CheckPtrExistence(State);
ValidateOscStateBank(State);
OneStateScan = State->OscillatorList;
while (OneStateScan != NIL)
{
OscStateRec* Temp;
(*OneStateScan->Template.DisposeState)(OneStateScan->StateReference);
Temp = OneStateScan;
OneStateScan = OneStateScan->Next;
Temp->Next = StateFreeList;
StateFreeList = Temp;
}
DisposeLFOGenerator(State->PitchLFO);
State->Next = StateBankFreeList;
StateBankFreeList = State;
}
/* get the reference to the note that this bank ties to. NIL if it doesn't */
struct NoteObjectRec* GetOscStateTieTarget(OscStateBankRec* State)
{
CheckPtrExistence(State);
ValidateOscStateBank(State);
return State->TieToNote;
}
/* perform one envelope clock cycle on a state bank. this returns True if the */
/* state bank is done and should be retired. (it will return false if it is a */
/* tie source.) */
MyBoolean UpdateOscStateBank(OscStateBankRec* State, long NumFrames,
largefixedsigned* OutputData)
{
OscStateRec* OneStateScan;
MyBoolean OscillatorsRunning;
float Frequency;
CheckPtrExistence(State);
ValidateOscStateBank(State);
if (State->Release1Countdown >= 0)
{
if (State->Release1Countdown == 0)
{
OneStateScan = State->OscillatorList;
while (OneStateScan != NIL)
{
(*OneStateScan->Template.KeyUpSustain1)(OneStateScan->StateReference);
OneStateScan = OneStateScan->Next;
}
LFOGeneratorKeyUpSustain1(State->PitchLFO);
}
State->Release1Countdown -= 1;
}
if (State->Release2Countdown >= 0)
{
if (State->Release2Countdown == 0)
{
OneStateScan = State->OscillatorList;
while (OneStateScan != NIL)
{
(*OneStateScan->Template.KeyUpSustain2)(OneStateScan->StateReference);
OneStateScan = OneStateScan->Next;
}
LFOGeneratorKeyUpSustain2(State->PitchLFO);
}
State->Release2Countdown -= 1;
}
if (State->Release3Countdown >= 0)
{
if (State->Release3Countdown == 0)
{
OneStateScan = State->OscillatorList;
while (OneStateScan != NIL)
{
(*OneStateScan->Template.KeyUpSustain3)(OneStateScan->StateReference);
OneStateScan = OneStateScan->Next;
}
LFOGeneratorKeyUpSustain3(State->PitchLFO);
}
State->Release3Countdown -= 1;
}
/* perform portamento */
if (State->PortamentoCounter > 0)
{
/* decrement is done before interpolation so that the final frequency */
/* will actually be reached. */
State->PortamentoCounter -= 1;
if (State->PortamentoHertz)
{
/* this transition is linear, so it's easy to compute */
/* L+F(R-L) */
State->CurrentFrequency = State->InitialFrequency
+ ((float)(State->TotalPortamentoTicks
- State->PortamentoCounter) / State->TotalPortamentoTicks)
* (State->FinalFrequency - State->InitialFrequency);
}
else
{
/* this transition is log-linear, so it's a bit messier */
State->CurrentFrequency = State->InitialFrequency * (float)FEXP(
((float)(State->TotalPortamentoTicks - State->PortamentoCounter)
/ State->TotalPortamentoTicks)
* (((float)FLN(State->FinalFrequency) / (float)LOG2)
- ((float)FLN(State->InitialFrequency) / (float)LOG2)) * LOG2);
}
}
/* update the pitch LFO modulation & figure out what the current pitch is */
if (State->PitchLFOStartCountdown > 0)
{
State->PitchLFOStartCountdown -= 1;
Frequency = State->CurrentFrequency;
}
else
{
/* do some pitch stuff */
Frequency = FastFixed2Float(LFOGenUpdateCycle(State->PitchLFO,
Double2FastFixed(State->CurrentFrequency)));
}
/* perform a cycle of resampling */
OscillatorsRunning = False;
OneStateScan = State->OscillatorList;
while (OneStateScan != NIL)
{
(*OneStateScan->Template.StateNewFrequency)(OneStateScan->StateReference,Frequency);
(*OneStateScan->Template.UpdateEnvelopes)(OneStateScan->StateReference);
(*OneStateScan->Template.GenSamples)(OneStateScan->StateReference,NumFrames,OutputData);
OscillatorsRunning = OscillatorsRunning || !(*OneStateScan->Template
.IsItFinished)(OneStateScan->StateReference);
OneStateScan = OneStateScan->Next;
}
return !OscillatorsRunning;
}